/*++

Module Name:

    ReadWriteRequest.cpp

Abstract:

    This module contains the implementation of the HID Read/Write request
    completion callback object.
--*/

#include "internal.h"
#include "Defines.h"
#include "SensorDDI.h"
#include "ReadWriteRequest.h"

#include "ReadWriteRequest.tmh"

/////////////////////////////////////////////////////////////////////////
//
// CReadWriteRequest::CReadWriteRequest
//
// Object constructor function
//
/////////////////////////////////////////////////////////////////////////
CReadWriteRequest::CReadWriteRequest() : 
    m_pHidWdfIoRequest(NULL),
    m_pHidWdfDevice(NULL),
    m_pHidWdfDriver(NULL),
    m_pHidWdfFile(NULL),
    m_pHidWdfIoTarget(NULL),
    m_pParentCallback(NULL),
    m_bytesRead(0),
    m_fResend(FALSE)
{

}

/////////////////////////////////////////////////////////////////////////
//
// CReadWriteRequest::~CReadWriteRequest
//
// Object destructor function
//
/////////////////////////////////////////////////////////////////////////
CReadWriteRequest::~CReadWriteRequest()
{

}


/////////////////////////////////////////////////////////////////////////
//
// CReadWriteRequest::InitializeRequest
//
// 
//
/////////////////////////////////////////////////////////////////////////
HRESULT CReadWriteRequest::InitializeRequest(IWDFDevice* pWdfDevice,
                                             CAsyncCallback* pCallback)
{
    HRESULT hr = (NULL != pWdfDevice) ? S_OK : E_UNEXPECTED;

    // Store the callback
    m_pParentCallback = pCallback;
    if(SUCCEEDED(hr))
    {
        m_fResend = TRUE;

        // Store the IWDFDevice pointer
        m_pHidWdfDevice = pWdfDevice;
        
        // Step 1: Try CreateWdfFile here (target to next driver in the driver stack)
        hr = m_pHidWdfDevice->CreateWdfFile( NULL, &m_pHidWdfFile );

        if(SUCCEEDED(hr) && (NULL != m_pHidWdfFile))
        {
            // Step 2: Get the parent driver object
            m_pHidWdfDevice->GetDriver( &m_pHidWdfDriver );

            // Step 3: Get the IO target i.e. default Target
            if (NULL != m_pHidWdfDriver)
            {
                m_pHidWdfDevice->GetDefaultIoTarget( &m_pHidWdfIoTarget );
            }
        }
    }

    return hr;
}

/////////////////////////////////////////////////////////////////////////
//
// CReadWriteRequest::UninitializeRequest
//
// 
//
/////////////////////////////////////////////////////////////////////////
HRESULT CReadWriteRequest::UninitializeRequest()
{
    m_bytesRead = 0;
    ZeroMemory(m_buffer, READ_BUFFER_SIZE);

    if (NULL != m_pHidWdfFile)
    {
        m_pHidWdfFile->Close();
    }

    return S_OK;
}

/////////////////////////////////////////////////////////////////////////
//
// CReadWriteRequest::OnCompletion
//
//  This method is called by Framework when read request is completed
//  The result is saved in the m_buffer
//
/////////////////////////////////////////////////////////////////////////
void CReadWriteRequest::OnCompletion(
        __in IWDFIoRequest*                 pRequest,
        __in IWDFIoTarget*                  pIoTarget,
        __in IWDFRequestCompletionParams*   CompletionParams,
        __in PVOID                          Context
        )
{
    UNREFERENCED_PARAMETER(pRequest);
    UNREFERENCED_PARAMETER(pIoTarget);
    UNREFERENCED_PARAMETER(Context);

    HRESULT hr = CompletionParams->GetCompletionStatus();
    WDF_REQUEST_TYPE reqType = CompletionParams->GetCompletedRequestType();

    //Delete the previous request object
    m_pHidWdfIoRequest->DeleteWdfObject();
    m_pHidWdfIoRequest = NULL;

    //Process the read buffer
    if ( SUCCEEDED( hr ) && (WdfRequestRead == reqType) )
    {
        m_bytesRead = (ULONG) CompletionParams->GetInformation();

        // Call back object process the buffer first
        if ( m_pParentCallback != NULL )
        {
			//
			// retrieve the context form this request if any
			//
			//void *context = NULL;
			//pRequest->RetrieveContext(&context);

            m_pParentCallback->OnAsyncReadCallback(m_buffer, m_bytesRead, Context);
        }

        // Queue up another pending Read request
        if ( TRUE == m_fResend )
        {
            CreateAndSendReadRequest();
        }
    }
    else if( SUCCEEDED( hr ) && (WdfRequestWrite == reqType) )
    {
        Trace(TRACE_LEVEL_INFORMATION, "WdfRequestWrite");
    }

    return;
}

/////////////////////////////////////////////////////////////////////////
//
// CReadWriteRequest::CreateAndSendReadRequest
//
//  This helper method creates and sends the read request down the stack
//  
//  Remarks:

// In this routine we:
//      1. Create a request and format it for read
//      2. Asynchronously send the request without any timeout
// 
//  In case of failure this routine deletes the request*/     
//
/////////////////////////////////////////////////////////////////////////
HRESULT CReadWriteRequest::CreateAndSendReadRequest(void *context)
{
    HRESULT hr = S_OK;
    CComPtr<IWDFMemory>      pOutputMemory = NULL;
    CComPtr<IRequestCallbackRequestCompletion> pICallback = NULL;

    //Reset internal buffer
    m_bytesRead = 0;
    ZeroMemory(m_buffer, READ_BUFFER_SIZE);

    //Creates a new request
    hr = m_pHidWdfDevice->CreateRequest(NULL, m_pHidWdfDevice, &m_pHidWdfIoRequest);

    //Get callback interface
    if ( SUCCEEDED( hr ) )
    {
        hr = this->QueryInterface( __uuidof(IRequestCallbackRequestCompletion) , (PVOID*)&pICallback );
 
        //Set completion callback
        if ( SUCCEEDED( hr ) )
        {
            m_pHidWdfIoRequest->SetCompletionCallback(pICallback, context);
        } 

		// set the context for the request
		//m_pHidWdfIoRequest->AssignContext(NULL, context);

        //Create output memory
        if ( SUCCEEDED( hr ) )
        {
            hr = m_pHidWdfDriver->CreatePreallocatedWdfMemory(  (BYTE*)m_buffer, 
                                                                 READ_BUFFER_SIZE, 
                                                                 NULL,                  // no object event callback
                                                                 m_pHidWdfIoRequest,    // request object as parent
                                                                 &pOutputMemory); 
        }

        //Format request
        if ( SUCCEEDED( hr ) )
        {
            hr = m_pHidWdfIoTarget->FormatRequestForRead( m_pHidWdfIoRequest, 
                                                          m_pHidWdfFile,
                                                          pOutputMemory, 
                                                          NULL, 
                                                          NULL );
        }

        //Send down the request
        if ( SUCCEEDED( hr ) )
        {
            hr = m_pHidWdfIoRequest->Send(  m_pHidWdfIoTarget,
                                            0,              //No flag
                                            0               //No timeout
                                            );
        }
        
        if( FAILED( hr ) )
        {    
            m_pHidWdfIoRequest->DeleteWdfObject();
            m_pHidWdfIoRequest = NULL;
        }
    }

    return hr;        
}

/////////////////////////////////////////////////////////////////////////
//
// CReadWriteRequest::CreateAndSendWriteRequest
//
//  This helper method creates and sends the write request down the stack
//  
//  Remarks:
//
// In this routine we:
//      1. Create a request and format it for write
//      2. Asynchronously send the request without any timeout
// 
//  In case of failure this routine deletes the request*/     
//
/////////////////////////////////////////////////////////////////////////
HRESULT CReadWriteRequest::CreateAndSendWriteRequest(
        BYTE* buffer,
        ULONG bufferSize,
		void *context
        )
{
    HRESULT hr = S_OK;

    CComPtr<IWDFMemory>                         pInputMemory = NULL;
    CComPtr<IRequestCallbackRequestCompletion>  pICallback = NULL;

    //Create a new request
    hr = m_pHidWdfDevice->CreateRequest(NULL, m_pHidWdfDevice, &m_pHidWdfIoRequest);

    if ( SUCCEEDED( hr ) )
    {
        hr = this->QueryInterface( __uuidof(IRequestCallbackRequestCompletion) , (PVOID*)&pICallback );
 
        //Set completion callback
        if ( SUCCEEDED( hr ) )
        {
            m_pHidWdfIoRequest->SetCompletionCallback(pICallback, context);
        } 

		// set the context for the request
		//m_pHidWdfIoRequest->AssignContext(NULL, context);

        //Create input memory
        if ( SUCCEEDED( hr ) )
        {
            hr = m_pHidWdfDriver->CreatePreallocatedWdfMemory(buffer, 
                                                             bufferSize, 
                                                             NULL,                  // no object event callback
                                                             m_pHidWdfIoRequest,    // request object as parent
                                                             &pInputMemory); 
        }

        //Format request
        if ( SUCCEEDED( hr ) )
        {
            hr = m_pHidWdfIoTarget->FormatRequestForWrite( m_pHidWdfIoRequest, 
                                                           m_pHidWdfFile,
                                                           pInputMemory, 
                                                           NULL, 
                                                           NULL );
        }

        //
        //Send down the request
        //    
        if ( SUCCEEDED( hr ) )
        {
            hr = m_pHidWdfIoRequest->Send(
                                        m_pHidWdfIoTarget,
                                        WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,   
                                        0                                       //No timeout
                                        );
        }
        
        if( FAILED( hr ) )
        {    
            m_pHidWdfIoRequest->DeleteWdfObject();
            m_pHidWdfIoRequest = NULL;
        }
    }

    return hr;        
}

/////////////////////////////////////////////////////////////////////////
//
// CReadWriteRequest::CreateAndSendIOCTLRequest
//
//  This helper method creates and sends the IOCTL request down the stack
//  
//  Remarks:
//
// In this routine we:
//      1. Create a request and format it for IOCTL
//      2. Asynchronously send the request without any timeout
// 
//  In case of failure this routine deletes the request*/     
//
/////////////////////////////////////////////////////////////////////////
HRESULT CReadWriteRequest::CreateAndSendIOCTLRequest(
		ULONG ulCode,
        BYTE* buffer,
        ULONG bufferSize,
		void *context
        )
{
    HRESULT hr = S_OK;

    CComPtr<IWDFMemory>                         pInputMemory = NULL;
    CComPtr<IRequestCallbackRequestCompletion>  pICallback = NULL;

    //Create a new request
    hr = m_pHidWdfDevice->CreateRequest(NULL, m_pHidWdfDevice, &m_pHidWdfIoRequest);

    if ( SUCCEEDED( hr ) )
    {
        hr = this->QueryInterface( __uuidof(IRequestCallbackRequestCompletion) , (PVOID*)&pICallback );
 
        //Set completion callback
        if ( SUCCEEDED( hr ) )
        {
            m_pHidWdfIoRequest->SetCompletionCallback(pICallback, context);
        } 

		// set the context for the request
		//m_pHidWdfIoRequest->AssignContext(NULL, context);

        //Create input memory
        if ( SUCCEEDED( hr ) )
        {
            hr = m_pHidWdfDriver->CreatePreallocatedWdfMemory(buffer, 
                                                             bufferSize, 
                                                             NULL,                  // no object event callback
                                                             m_pHidWdfIoRequest,    // request object as parent
                                                             &pInputMemory); 
        }

        //Format request
        if ( SUCCEEDED( hr ) )
        {
            hr = m_pHidWdfIoTarget->FormatRequestForIoctl( m_pHidWdfIoRequest,
														   ulCode,
                                                           m_pHidWdfFile,
                                                           pInputMemory, 
                                                           NULL, 
                                                           NULL,
														   NULL);
        }

        //
        //Send down the request
        //    
        if ( SUCCEEDED( hr ) )
        {
            hr = m_pHidWdfIoRequest->Send(
                                        m_pHidWdfIoTarget,
                                        WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,   
                                        0                                       //No timeout
                                        );
        }
        
        if( FAILED( hr ) )
        {    
            m_pHidWdfIoRequest->DeleteWdfObject();
            m_pHidWdfIoRequest = NULL;
        }
    }

    return hr;        
}

/////////////////////////////////////////////////////////////////////////
//
// CReadWriteRequest::CancelAndStopPendingRequest
//
//   
//
/////////////////////////////////////////////////////////////////////////
HRESULT CReadWriteRequest::CancelAndStopPendingRequest()
{
    HRESULT hr = S_OK;

    if ( NULL != m_pHidWdfIoRequest )
    {
        m_pHidWdfIoRequest->CancelSentRequest();
    }

    return hr;
}
